package name

import (
	"fmt"
	"io/ioutil"

	"gitee.com/thubcc/blockchain/ethclient"
	"github.com/ethereum/go-ethereum/common"
)

type Config struct {
	Url      string
	AbiFile  string
	Addr     common.Address
	Contract common.Address
	ChainID  int64
}

type NameService struct {
	Config
	contract *ethclient.Contract
	done     chan struct{}
	db 		 map[common.Address]string
}

func(config *Config) NewNameService() (*NameService, error) {

	ret := NameService{Config:*config}

	clientConfig := ethclient.NewConfig(nil,config.ChainID,config.Url)
	
	ext,err := clientConfig.NewExt(nil)
	if err != nil {
		return nil,fmt.Errorf("Connect chain failure",err)
	}
	
	abiFile, err := ioutil.ReadFile(config.AbiFile)
	if err != nil {
		return nil,fmt.Errorf("read abi failure",err)
	}

	ret.contract, err = ext.NewContract(abiFile,config.Contract)
	if err != nil {
		return nil,fmt.Errorf("initial contract failure",err)
	}

	ret.done = make(chan struct{})
	ret.db   = make(map[common.Address]string)
	
	return &ret,nil
}

func(n *NameService) AddressToName(addr common.Address) (string,error) {
	name := ""
	_, err := n.contract.Call(&name, n.Addr,"get",addr)
	if err != nil {
		return "",fmt.Errorf("get name failure",err)
	}
	if name == "" {
		return "",fmt.Errorf("get nil name",err)
	}
	return name,nil
}

func(n *NameService) Run(q chan common.Address, r chan map[common.Address]string) {
	for {
		select {
		case addr := <-q:
			name,err := n.AddressToName(addr)
			if err==nil {
				n.db[addr] = name
				r <- n.db
			} else {
				fmt.Println("Get Name failure",err)
			}
		case <-n.done:
			return
		}
	}
}

func(n *NameService) Query(addr common.Address,force bool) string {
	if name,ok := n.db[addr]; !ok || force {
		name,err := n.AddressToName(addr)
		if err==nil {
			n.db[addr] = name
			return name
		} else {
			return addr.Hex()
		}	
	} else {
		return name
	}
}

func(n *NameService) Stop() {
	n.done <- struct{}{}
}

func(n *NameService) Close() {
	n.contract.Close()
}